package com.orm.mybatisplus.config;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.orm.mybatisplus.util.DbUtil;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 数据源配置
 */
@Configuration
// 扫描 Mapper 接口并容器管理
@MapperScan(basePackages = DatasourceConfig.PACKAGE, sqlSessionTemplateRef = "sqlSessionTemplate")
public class DatasourceConfig {
    // mapper扫描
    static final String PACKAGE = "com.orm.mybatisplus.mapper.*";
    static final String MAPPER_LOCATION = "classpath:mapper/*/*.xml";

    @Value("${master1.url}")
    private String url1;

    @Value("${master1.username}")
    private String user1;

    @Value("${master1.password}")
    private String password1;

    @Value("${master1.driverClassName}")
    private String driverClass1;

    @Value("${master2.url}")
    private String url2;

    @Value("${master2.username}")
    private String user2;

    @Value("${master2.password}")
    private String password2;

    @Value("${master2.driverClassName}")
    private String driverClass2;

    @Value("${oracle.url}")
    private String oracleUrl;

    @Value("${oracle.username}")
    private String oracleUser;

    @Value("${oracle.password}")
    private String oraclePassword;

    @Value("${oracle.driverClassName}")
    private String oracleDriverClass;

    @Bean(name = "dynamicDataSource")
    @Primary
    public DynamicDataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = DynamicDataSource.getInstance();

        // master1数据源
        DataSource dataSourceMaster1 = initDataSource(driverClass1,url1,user1,password1);  //非分布式事务锁
        // master2数据源
        DataSource dataSourceMaster2 = initDataSource(driverClass2,url2,user2,password2);  //非分布式事务锁
        // master3数据源
        DataSource dataSourceMaster3 = initDataSource(oracleDriverClass,oracleUrl,oracleUser,oraclePassword); //非分布式事务锁

        Map<Object,Object> map = new HashMap<>();
        map.put(DbUtil.DB_MASTER1, dataSourceMaster1);
        map.put(DbUtil.DB_MASTER2, dataSourceMaster2);
        map.put(DbUtil.DB_MASTER3, dataSourceMaster3);
        dynamicDataSource.setTargetDataSources(map);
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(dataSourceMaster1);
        return dynamicDataSource;
    }

    /**   非分布式事务
     * 初始数据源
     * @param driverClass    驱动
     * @param url       数据库连接
     * @param user  用户名
     * @param password  密码
     * @return
     */
    public HikariDataSource initDataSource(String driverClass, String url, String user, String password){
        //jdbc配置
        HikariDataSource hikariDataSource = new HikariDataSource();
        hikariDataSource.setDriverClassName(driverClass);
        hikariDataSource.setJdbcUrl(url);
        hikariDataSource.setUsername(user);
        hikariDataSource.setPassword(password);
        setPool(hikariDataSource);
        return hikariDataSource;
    }

    /**   非事务分布式
     * 连接池配置
     * @param hikariDataSource
     */
    private void setPool(HikariDataSource hikariDataSource){
        //连接池配置
        hikariDataSource.setMinimumIdle(5);
        hikariDataSource.setMaximumPoolSize(20);
        hikariDataSource.setAutoCommit(true);
        hikariDataSource.setPoolName("SpringBootDemoHikariCP");
        hikariDataSource.setMaxLifetime(1800000);
        hikariDataSource.setIdleTimeout(600000);
        hikariDataSource.setConnectionTimeout(30000);
    }

    /**
     * 将动态数据源添加到事务管理器中，并生成新的bean
     *
     * 切库与事务注意:
     *   1.有@Transactional注解的方法，方法内部不可以做切换数据库 操作
     *   2.在同一个service其他方法调用带@Transactional的方法，事务不起作用
     *   3.在应用中因为使用了 DAO 层的切面切换数据源，所以 @Transactional 注解不能加在类上，只能用于方法；有 @Trasactional注解的方法无法切换数据源
     * @return 事务管理实例
     */
//    @Bean(name = "transactionManager")    //非分布式事务锁
//    @Primary
//    public DataSourceTransactionManager transactionManager() {
//        return new DataSourceTransactionManager(dynamicDataSource());
//    }

    @Bean(name = "sqlSessionFactory")
    @Primary
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        sessionFactory.setDataSource(dynamicDataSource);
        sessionFactory.setTypeAliasesPackage("com.orm.mybatisplus.entity");
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setMapUnderscoreToCamelCase(true);
        sessionFactory.setConfiguration(configuration);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(DatasourceConfig.MAPPER_LOCATION));
        return sessionFactory.getObject();
    }

    /**
     * 设置动态数据源DynamicDataSource到会话工厂
     */
    @Bean(name = "sqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    /**
     * 将动态数据源添加到事务管理器中，并生成新的bean
     *
     * 切库与事务注意:
     *   1.有@Transactional注解的方法，方法内部不可以做切换数据库 操作
     *   2.在同一个service其他方法调用带@Transactional的方法，事务不起作用
     *   3.在应用中因为使用了 DAO 层的切面切换数据源，所以 @Transactional 注解不能加在类上，只能用于方法；有 @Trasactional注解的方法无法切换数据源
     * @return 事务管理实例
     */
    @Bean
    public PlatformTransactionManager platformTransactionManager(@Qualifier("dynamicDataSource") DataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }



}